/* Copyright (C) 2015-2016 HangZhou Zenzet Technology Co., Ltd.
 * All right reserved

 * File:crypto/locketfilecrypt.c
 * Author:guojianchuan/max
 * Date:2016-07-20

 */

/* system header */
#include <stdio.h>
#include <string.h>

/* 3rd project header */
#include "openssl/evp.h"
#include "openssl/aes.h"
#include "cJSON.h"

/* my project header */
#include "locketdef.h"
#include "locketerrno.h"
#include "locketlog.h"
#include "locketcommon.h"

#include "locketfilecrypt.h"

/**
* @Function:LOCKET_CIPHER_CheckFileEncryptParam
* @Author: guojianchuan/max
* @Date: 2016-07-18
* @Description: Check param
* @caution: 
*/
static int LOCKET_CIPHER_CheckFileEncryptParam (IN CIPHER_FILE_S *pstFileCrypt, IN META_S *pstMeta)
{
    if ((NULL == pstFileCrypt) ||
        (NULL == pstFileCrypt->pcFileName) ||
        (NULL == pstFileCrypt->pfCryptCBFunc) ||
        (NULL == pstMeta))
    {
        LOCKET_LOGERROR ("input param is valid");
        return ERR_COMMON_INVALID_PARAM;
    }

    return ERR_COMMON_SUCCESS;
}

/**
* @Function:LOCKET_CIPHER_GenMeta
* @Author: guojianchuan/max
* @Date: 2016-07-18
* @Description: 生成元信息(JSON格式)
* @caution: 
*/
static int LOCKET_CIPHER_GenMeta(IN META_S* pstMeta, OUT char *pcMeta)
{
    if ((NULL == pstMeta) || (NULL == pcMeta))
    {
        return ERR_COMMON_INVALID_PARAM;
    }

    int iErr = ERR_COMMON_SUCCESS;

    cJSON *root,*cipher, *thumb;

    /* root节点字段 */
    root=cJSON_CreateObject();    
    if (NULL == root)
    {
        return ERR_FILE_JSON_OPER_FAILED;
    }
    cJSON_AddStringToObject(root, "id", pstMeta->szId);
    cJSON_AddStringToObject(root, "owner", pstMeta->szOwner);
    cJSON_AddNumberToObject(root,"version", pstMeta->iVersion);
    cJSON_AddNumberToObject(root,"blockSize", pstMeta->iBlockSize);
    cJSON_AddNumberToObject(root,"offline", pstMeta->iOffLine);

    /* 增加cipher字段 */
    CIPHER_S *pstCipher = &pstMeta->stCipher;
    cipher = cJSON_CreateObject();
    if (NULL == cipher)
    {
        return ERR_FILE_JSON_OPER_FAILED;
    }
    cJSON_AddItemToObject(root, "cipher", cipher);
    cJSON_AddStringToObject(cipher,"algorithm", pstCipher->szAlgorithm);
    cJSON_AddStringToObject(cipher,"mode", pstCipher->szMode);
    cJSON_AddStringToObject(cipher,"padding", pstCipher->szPadding);
    cJSON_AddStringToObject(cipher,"encryptedKey", (char*) pstCipher->szKey);
    cJSON_AddStringToObject(cipher,"encryptedIv", (char*) pstCipher->szIv);

    /* 缩略图字段 */
    THUMB_S *pstThumb = &pstMeta->stThumb;
    thumb = cJSON_CreateObject();
    if (NULL == thumb)
    {
        return ERR_FILE_JSON_OPER_FAILED;
    }
    cJSON_AddItemToObject(root, "thumb", thumb);
    cJSON_AddFalseToObject(thumb,"exists");
    cJSON_AddNumberToObject(thumb, "size", pstThumb->iSize);

    /* 生成的JSON文本 */
    char *pcOutput = cJSON_Print(root);
    memcpy (pcMeta, pcOutput, strlen(pcOutput));

    /* 资源回收 */
    cJSON_Delete(root);

    return ERR_COMMON_SUCCESS;
}

/**
* @Function:LOCKET_CIPHER_BuildHeaderMeta
* @Author: guojianchuan/max
* @Date: 2016-07-18
* @Description: 构造文件头和meta(带头尾标记)
* @caution: 
*/
int LOCKET_CIPHER_BuildHeaderMeta(IN META_S* pstMeta, OUT char *pcOutput, OUT int *piOutLen)
{
    if ((NULL == pstMeta) || (NULL == pcOutput))
    {
        return ERR_COMMON_INVALID_PARAM;
    }

    int iErr = ERR_COMMON_SUCCESS;

    /* 生成meta信息 */
    char szMeta[META_SIZE] = {0};
    iErr = LOCKET_CIPHER_GenMeta (pstMeta, szMeta);
    if (ERR_COMMON_SUCCESS != iErr)
    {
        LOCKET_LOGERROR ("Generate meta failed. err:%d", iErr);
        return iErr;
    }

    char *pcOffSet = pcOutput;

    /* 添加文件头 */
    char szHeaderPrefixTag[HEADER_TAG_LEN] = HEADER_PREFIX_TAG;
    memcpy (pcOffSet, szHeaderPrefixTag, sizeof (szHeaderPrefixTag));
    pcOffSet += sizeof (szHeaderPrefixTag);

    /* 添加meta头长度 */
    char szMetaSize[META_LEN_SIZE + 1] = {0};
    snprintf (szMetaSize, sizeof (szMetaSize), "%05lu", strlen(szMeta) + 2*META_TAG_LEN);
    memcpy (pcOffSet, szMetaSize, strlen (szMetaSize));
    pcOffSet += strlen (szMetaSize);

    /* 添加meta头 */
    char szMetaPrefixTag[META_TAG_LEN] = META_PREFIX_TAG;
    memcpy (pcOffSet, szMetaPrefixTag, sizeof (szMetaPrefixTag));
    pcOffSet += sizeof (szMetaPrefixTag);

    /* 添加meta信息 */
    memcpy (pcOffSet, szMeta, strlen(szMeta));
    pcOffSet += strlen(szMeta);

    /* 添加meta尾 */
    char szMetaSuffixTag[META_TAG_LEN] = META_SUFFIX_TAG;
    memcpy (pcOffSet, szMetaSuffixTag, sizeof (szMetaSuffixTag));
    pcOffSet += sizeof (szMetaSuffixTag);

    *piOutLen = (pcOffSet - pcOutput);

    return ERR_COMMON_SUCCESS;
}

/**
* @Function:LOCKET_CIPHER_FileEncrypt
* @Author: guojianchuan/max
* @Date: 2016-07-18
* @Description: AES encrypt for file
* @caution: 阻塞接口, 若需要使用非阻塞, 可以在线程内调用该接口
*/
int LOCKET_CIPHER_FileEncrypt(IN CIPHER_FILE_S *pstFileCrypt, IN META_S *pstMeta)
{
    int iErr = ERR_COMMON_SUCCESS;

    iErr = LOCKET_CIPHER_CheckFileEncryptParam (pstFileCrypt, pstMeta);
    if (ERR_COMMON_SUCCESS != iErr)
    {
        return iErr;
    }

    /* 打开待加密的文件 */
    FILE *fp = LOCKET_OpenFile (pstFileCrypt->pcFileName, "rb");
    if (NULL == fp)
    {
        iErr = ERR_COMMON_FILE_OPER_FAILED;
        LOCKET_LOGERROR ("open[%s] failed. err:%d", pstFileCrypt->pcFileName, iErr);
        return iErr;
    }

    unsigned long long udlFileSize = LOCKET_GetFileSize (pstFileCrypt->pcFileName);
    if (0 == udlFileSize)
    {
        return ERR_COMMON_FILE_OPER_FAILED;
    }

    const EVP_CIPHER *pstCipher  = NULL;
    EVP_CIPHER_CTX *pstCipherCtx = NULL;
    unsigned char *pucBuffer = NULL;
    do 
    {
        /* 初始化加密算法 */
        pstCipher    = EVP_get_cipherbyname(CRYPT_ALGORITHM);
        pstCipherCtx = EVP_CIPHER_CTX_new ();
        iErr = EVP_CipherInit_ex(pstCipherCtx, pstCipher, NULL, pstMeta->stCipher.szKey, pstMeta->stCipher.szIv, 1);
        if (ERR_COMMON_SUCCESS != iErr)
        {
            LOCKET_LOGERROR ("EVP_CipherInit_ex failed. %s", LOCKET_ERR_GetString());
            break;
        }

        /* 构造文件头, 文件meta信息 */
        int iHeaderMetaLen = 0;
        char szHeaderMeta[HEADERMETA_SIZE] = {0};
        iErr = LOCKET_CIPHER_BuildHeaderMeta (pstMeta, szHeaderMeta, &iHeaderMetaLen);
        if (ERR_COMMON_SUCCESS != iErr)
        {
            LOCKET_LOGERROR ("build header/meta failed. err:%d", iErr);
            break;
        }
        CB_PARAM_S stParam = {0};
        stParam.pcData   = szHeaderMeta;
        stParam.iParamLen = iHeaderMetaLen;
        (*pstFileCrypt->pfCryptCBFunc) (MSG_CRYPT_DATA, (void*) &stParam);

        /* 构造密文头 */
        char szCipherHeader[ENCRYPT_TAG_LEN] = ENCRYPT_PREFIX_TAG;
        stParam.pcData   = szCipherHeader;
        stParam.iParamLen = sizeof (szCipherHeader);
        (*pstFileCrypt->pfCryptCBFunc) (MSG_CRYPT_DATA, (void*) &stParam);

        unsigned char *pucInputBuffer  = NULL, *pucOutputBuffer = NULL;
        pucBuffer  = (unsigned char*) LOCKET_malloc (pstMeta->iBlockSize * 2);
        if (NULL == pucBuffer)
        {
            break;
        }
        pucInputBuffer = pucBuffer;
        pucOutputBuffer = pucBuffer + pstMeta->iBlockSize;

        /* 分段加密 */
        int iBlocks   = CALC_BLOCK_NUM (udlFileSize, pstMeta->iBlockSize);
        //unsigned long long udlTotalLen = 0;
        int i = 0, iReadLen = 0, iOutLen = 0;
        float fProgress = 0.0;
        for (i = 0; i < iBlocks; i++)
        {
            iReadLen = LOCKET_ReadFile (pucInputBuffer, 1, pstMeta->iBlockSize, fp);
            if (iReadLen < 0)
            {
                iErr = ERR_COMMON_FILE_OPER_FAILED;
                LOCKET_LOGERROR ("read failed. err:%d", iErr);
                /* read failed or read EOF */
                break;
            }
            else if (0 == iReadLen)
            {
                break;
            }
            //udlTotalLen+= iReadLen;
            iErr = EVP_CipherUpdate(pstCipherCtx, pucOutputBuffer, &iOutLen, pucInputBuffer, iReadLen);
            if (ERR_COMMON_SUCCESS != iErr)
            {
                LOCKET_LOGERROR ("EVP_CipherUpdate failed. %s", LOCKET_ERR_GetString());
                break;
            }

            stParam.pcData    = (char*) pucOutputBuffer;
            stParam.iParamLen  = iOutLen;
            iErr = (*pstFileCrypt->pfCryptCBFunc) (MSG_CRYPT_DATA, (void*) &stParam);
            if (ERR_COMMON_SUCCESS != iErr)
            {
                break;
            }

            if (NULL != pstFileCrypt->pfProgressCBFunc)
            {
                fProgress = GET_PROGRESS (i + 1, iBlocks);
                (*pstFileCrypt->pfProgressCBFunc) (MSG_CRYPT_PROCESS, (void*) &fProgress);
            }
        }

        int iOutLen2 = 0;
        iErr = EVP_CipherFinal_ex(pstCipherCtx, pucOutputBuffer + iOutLen, &iOutLen2);
        if (ERR_COMMON_SUCCESS != iErr)
        {
            LOCKET_LOGERROR ("EVP_CipherFinal_ex failed. %s", LOCKET_ERR_GetString());
            break;
        }
        stParam.pcData    = (char*) pucOutputBuffer;
        stParam.iParamLen  = iOutLen2;
        (*pstFileCrypt->pfCryptCBFunc) (MSG_CRYPT_DATA, (void*) &stParam);

        /* 构造密文尾 */
        char szCipherEnd[ENCRYPT_TAG_LEN] = ENCRYPT_SUFFIX_TAG;
        stParam.pcData   = szCipherEnd;
        stParam.iParamLen = sizeof (szCipherEnd);
        (*pstFileCrypt->pfCryptCBFunc) (MSG_CRYPT_DATA, (void*) &stParam);
    } while (0);

    (*pstFileCrypt->pfCryptCBFunc) (MSG_CRYPT_FINISH, NULL);

    /* 回收资源 */
    LOCKET_free ((void**)&pucBuffer);
    EVP_CIPHER_CTX_free (pstCipherCtx);
    fclose (fp);

    return iErr;
}

/**
* @Function:LOCKET_CIPHER_CheckFileDecryptParam
* @Author: guojianchuan/max
* @Date: 2016-07-20
* @Description: Check param
* @caution: 
*/
static int LOCKET_CIPHER_CheckFileDecryptParam(IN CIPHER_FILE_S *pstFileCrypt)
{
    if ((NULL == pstFileCrypt) || 
        (NULL == pstFileCrypt->pcFileName) ||
        (NULL == pstFileCrypt->pfCryptCBFunc))
    {
        LOCKET_LOGERROR ("input param is valid");
        return ERR_COMMON_INVALID_PARAM;
    }

    return ERR_COMMON_SUCCESS;
}

/**
* @Function:LOCKET_CIPHER_CheckFileHeader
* @Author: guojianchuan/max
* @Date: 2016-07-20
* @Description: 检查文件头是否有效
* @caution: 返回值 1:有效 0:无效
*/
static int LOCKET_CIPHER_CheckFileHeader (IN char *pcFileHeader)
{
    if (NULL == pcFileHeader)
    {
        return 0;
    }

    char szFileHeaerTag[HEADER_SIZE] = HEADER_PREFIX_TAG;
    return (0 == memcmp (pcFileHeader, szFileHeaerTag, sizeof (szFileHeaerTag))) ? 1 : 0;
}

/**
* @Function:LOCKET_CIPHER_CheckMetaHeader
* @Author: guojianchuan/max
* @Date: 2016-07-20
* @Description: 检查Meta头是否有效
* @caution: 返回值 1:有效 0:无效
*/
static int LOCKET_CIPHER_CheckMetaHeader (IN char *pcMetaHeader)
{
    if (NULL == pcMetaHeader)
    {
        return 0;
    }

    char szMetaPrefixTag[META_TAG_LEN] = META_PREFIX_TAG;
    return (0 == memcmp (pcMetaHeader, szMetaPrefixTag, sizeof (szMetaPrefixTag))) ? 1 : 0;
}

/**
* @Function:LOCKET_CIPHER_CheckCipherHeader
* @Author: guojianchuan/max
* @Date: 2016-07-20
* @Description: 检查密文头是否有效
* @caution: 返回值 1:有效 0:无效
*/
static int LOCKET_CIPHER_CheckCipherHeader (IN char *pcCipherHeader)
{
    if (NULL == pcCipherHeader)
    {
        return 0;
    }

    char szCipherHeader[ENCRYPT_TAG_LEN] = ENCRYPT_PREFIX_TAG;
    return (0 == memcmp (pcCipherHeader, szCipherHeader, sizeof (szCipherHeader))) ? 1 : 0;
}

/**
* @Function:LOCKET_CIPHER_ParseFileMeta
* @Author: guojianchuan/max
* @Date: 2016-07-20
* @Description: 解析元信息
* @caution: 
*/
int LOCKET_CIPHER_ParseFileMeta(IN char *pcMeta, OUT META_S *pstMeta)
{
    if ((NULL == pcMeta) || (NULL == pstMeta))
    {
        return ERR_COMMON_INVALID_PARAM;
    }

    int iErr = ERR_COMMON_SUCCESS;

    cJSON *root = cJSON_Parse(pcMeta);
    if (NULL == root)
    {
        return ERR_FILE_JSON_OPER_FAILED;
    }

    char *node = NULL;
    cJSON *tmp = NULL;

    /* 获取id */
    node = "id";
    tmp = cJSON_GetObjectItem(root, node);
    if (NULL != tmp)
    {
        char *pcId = tmp->valuestring;
        snprintf (pstMeta->szId, sizeof (pstMeta->szId), "%s", pcId);
    }
    else
    {
        LOCKET_LOGERROR ("[cJSON] can not find node:%s", node);
    }

    /* 获取owner */
    node = "owner";
    tmp = cJSON_GetObjectItem(root, node);
    if (NULL != tmp)
    {
        char *pcOwner = tmp->valuestring;
        snprintf (pstMeta->szOwner, sizeof (pstMeta->szOwner), "%s", pcOwner);
    }
    else
    {
        LOCKET_LOGERROR ("[cJSON] can not find node:%s", node);
    }

    /* 获取version */
    node = "version";
    tmp = cJSON_GetObjectItem(root, node);
    if (NULL != tmp)
    {
        pstMeta->iVersion = tmp->valueint;
    }
    else
    {
        LOCKET_LOGERROR ("[cJSON] can not find node:%s", node);
    }

    /* 获取blockSize */
    node = "blockSize";
    tmp = cJSON_GetObjectItem (root, node);
    if (NULL != tmp)
    {
        pstMeta->iBlockSize = tmp->valueint;
    }
    else 
    {
        LOCKET_LOGERROR ("[cJSON] can not find node:%s", node);
    }

    /* 获取offline */
    node = "offline";
    tmp = cJSON_GetObjectItem (root, node);
    if (NULL != tmp)
    {
        pstMeta->iOffLine = tmp->valueint;
    }
    else
    {
        LOCKET_LOGERROR ("[cJSON] can not find node:%s", node);
    }

    cJSON *cipher = cJSON_GetObjectItem(root,"cipher");
    if (NULL == cipher)
    {
        return ERR_FILE_JSON_OPER_FAILED;
    }
    CIPHER_S *pstCipher = &pstMeta->stCipher;

    /* 获取algorithm */
    node = "algorithm";
    tmp = cJSON_GetObjectItem (cipher, node);
    if (NULL != tmp)
    {
        char *pcAlgorithm = tmp->valuestring;
        snprintf (pstCipher->szAlgorithm, sizeof (pstCipher->szAlgorithm), "%s", pcAlgorithm);
    }
    else
    {
        LOCKET_LOGERROR ("[cJSON] can not find node:%s", node);
    }

    /* 获取mode */
    node = "mode";
    tmp = cJSON_GetObjectItem (cipher, node);
    if (NULL != tmp)
    {
        char *pcMode = tmp->valuestring;
        snprintf (pstCipher->szMode, sizeof (pstCipher->szMode), "%s", pcMode);
    }
    else
    {
        LOCKET_LOGERROR ("[cJSON] can not find node:%s", node);
    }

    /* 获取padding */
    node = "padding";
    tmp = cJSON_GetObjectItem(cipher, node);
    if (NULL != tmp)
    {
        char *pcPadding = tmp->valuestring;
        snprintf (pstCipher->szPadding, sizeof (pstCipher->szPadding), "%s", pcPadding);
    }
    else
    {
        LOCKET_LOGERROR ("[cJSON] can not find node:%s", node);
    }
    
    /* 获取key */
    node = "encryptedKey";
    tmp = cJSON_GetObjectItem(cipher, node);
    if (NULL != tmp)
    {
        char *pcKey = tmp->valuestring;
        snprintf ((char*) pstCipher->szKey, sizeof (pstCipher->szKey), "%s", pcKey);
    }
    else
    {
        LOCKET_LOGERROR ("[cJSON] can not find node:%s", node);
    }

    /* 获取Iv */
    node = "encryptedIv";
    tmp = cJSON_GetObjectItem (cipher, node);
    if (NULL != tmp)
    {
        char *pcIv = tmp->valuestring;
        snprintf ((char*) pstCipher->szIv, sizeof (pstCipher->szIv), "%s", pcIv);
    }
    else
    {
        LOCKET_LOGERROR ("[cJSON] can not find node:%s", node);
    }

    cJSON *thumb = cJSON_GetObjectItem(root,"thumb");
    if (NULL == thumb)
    {
        /* thumb是可选字段，所以返回SUCCESS */
        LOCKET_LOGERROR ("[cJSON] can not find node:%s", node);
        return ERR_COMMON_SUCCESS;
    }
    THUMB_S *pstThumb = &pstMeta->stThumb;

    /* 获取existes */
    node = "exists";
    tmp = cJSON_GetObjectItem (thumb, node);
    if (NULL != tmp)
    {
        pstThumb->iExistes = tmp->valueint;
    }
    else
    {
        LOCKET_LOGERROR ("[cJSON] can not find node:%s", node);
    }

    /* 获取size */
    node = "size";
    tmp = cJSON_GetObjectItem (thumb, node);
    if (NULL != tmp)
    {
        pstThumb->iSize = tmp->valueint;
    }
    else
    {
        LOCKET_LOGERROR ("[cJSON] can not find node:%s", node);
    }

    /* 资源回收 */
    cJSON_Delete (root);

    return ERR_COMMON_SUCCESS;
}

/**
* @Function:LOCKET_CIPHER_MoveToDataPos
* @Author: guojianchuan/max
* @Date: 2016-07-26
* @Description: 获取文件中加密数据的开始偏移
* @caution: 
*/
static int LOCKET_CIPHER_MoveToDataPos (IN FILE  *fp, OUT META_S *pstMeta)
{
    int iErr     = ERR_COMMON_SUCCESS;
    int iReadLen = 0;

    do
    {
        /* 检查文件头 */
        {
            char szHeader[HEADER_TAG_LEN] = {0};
            iReadLen = LOCKET_ReadFile (szHeader, 1, sizeof (szHeader), fp);
            if (iReadLen != sizeof (szHeader))
            {
                iErr = ERR_COMMON_FILE_OPER_FAILED;
                break;
            }
            if (!LOCKET_CIPHER_CheckFileHeader (szHeader))
            {
                iErr = ERR_FILE_INVALID_HEADER;
                LOCKET_LOGERROR ("Parse file meta failed, err:%d", iErr);
                break;
            }
        }

        /* 获取meta长度 */
        int iMetaSize = 0;
        {
            char szMetaSize[META_LEN_SIZE + 1] = {0};
            iReadLen = LOCKET_ReadFile (szMetaSize, 1, META_LEN_SIZE, fp);
            if (iReadLen != META_LEN_SIZE)
            {
                iErr = ERR_COMMON_FILE_OPER_FAILED;
                break;
            }
            iMetaSize = atoi (szMetaSize);
            if (iMetaSize == 0)
            {
                iErr = ERR_FILE_INVALID_METASIZE;
                break;
            }
        }

        /* 检查meta头  */
        char szMeta[META_SIZE] = {0};
        {
            iReadLen = LOCKET_ReadFile (szMeta, 1, iMetaSize, fp);
            if (iReadLen != iMetaSize)
            {
                iErr = ERR_COMMON_FILE_OPER_FAILED;
                break;
            }

            char szMetaHeader[META_TAG_LEN] = {0};
            memcpy (szMetaHeader, szMeta, sizeof (szMetaHeader));
            if (!LOCKET_CIPHER_CheckMetaHeader (szMetaHeader))
            {
                iErr = ERR_FILE_INVALID_HEADER;
                LOCKET_LOGERROR ("Parse file meta failed, err:%d", iErr);
                break;
            }
        }

        /* 解析meta信息 */
        {
            char szMetaBody[META_SIZE] = {0};
            memcpy (szMetaBody, szMeta + META_TAG_LEN, (iMetaSize - 2 * META_TAG_LEN));
            iErr = LOCKET_CIPHER_ParseFileMeta (szMetaBody, pstMeta);
            if (ERR_COMMON_SUCCESS != iErr)
            {
                LOCKET_LOGERROR ("Parse file meta failed, err:%d", iErr);
                break;
            }
        }

        /* 检查密文头 */
        {
            char szCipherHeader[ENCRYPT_TAG_LEN] = {0};
            iReadLen = LOCKET_ReadFile (szCipherHeader, 1, sizeof (szCipherHeader), fp);
            if (iReadLen != sizeof (szCipherHeader))
            {
                iErr = ERR_COMMON_FILE_OPER_FAILED;
                break;
            }

            if (!LOCKET_CIPHER_CheckCipherHeader (szCipherHeader))
            {
                iErr = ERR_FILE_INVALID_CIPHER;
                LOCKET_LOGERROR ("Parse file meta failed, err:%d", iErr);
                break;
            }
        }
    } while (0);

    return iErr;
}

/**
* @Function:LOCKET_CIPHER_FileDecrypt
* @Author: guojianchuan/max
* @Date: 2016-07-18
* @Description: AES decrypt for file
* @caution: 阻塞接口, 若需要使用非阻塞, 可以在线程内调用该接口
*/
int LOCKET_CIPHER_FileDecrypt(IN CIPHER_FILE_S *pstFileCrypt)
{
    int iErr = ERR_COMMON_SUCCESS;

    iErr = LOCKET_CIPHER_CheckFileDecryptParam(pstFileCrypt);
    if (ERR_COMMON_SUCCESS != iErr)
    {
        return iErr;
    }

    /* 打开文件 */
    FILE *fp = LOCKET_OpenFile (pstFileCrypt->pcFileName, "rb");
    if (NULL == fp)
    {
        iErr = ERR_COMMON_FILE_OPER_FAILED;
        LOCKET_LOGERROR ("open[%s] failed. err:%d", pstFileCrypt->pcFileName, iErr);
        return iErr;
    }

    const EVP_CIPHER *pstCipher  = NULL;
    EVP_CIPHER_CTX *pstCipherCtx = NULL;
    unsigned char *pucBuffer     = NULL;
    int iReadLen                 = 0;
    CB_PARAM_S stParam;
    do 
    {
        /* 获取文件大小 */
        unsigned long long udlFileSize = LOCKET_GetFileSize (pstFileCrypt->pcFileName);

        /* 转移fp读指针到实际密文开始的地方 */
        META_S stMeta;
        memset (&stMeta, 0, sizeof (stMeta));
        iErr = LOCKET_CIPHER_MoveToDataPos (fp, &stMeta);
        if (ERR_COMMON_SUCCESS != iErr)
        {
            break;
        }

        /* 获取密文数据大小 */
        unsigned long long udlOffSet = ftell (fp);
        unsigned long long udlCipherDataSize = udlFileSize - udlOffSet - ENCRYPT_TAG_LEN;
        int iBlocks   = CALC_BLOCK_NUM (udlCipherDataSize, stMeta.iBlockSize);

        unsigned char *pucInputBuffer  = NULL, *pucOutputBuffer = NULL;
        pucBuffer  = (unsigned char*) LOCKET_malloc (stMeta.iBlockSize * 2);
        if (NULL == pucBuffer)
        {
            break;
        }
        pucInputBuffer  = pucBuffer;
        pucOutputBuffer = pucBuffer + stMeta.iBlockSize;

        /* 初始化加密算法 */
        pstCipher    = EVP_get_cipherbyname(CRYPT_ALGORITHM);
        pstCipherCtx = EVP_CIPHER_CTX_new ();
        iErr = EVP_CipherInit_ex(pstCipherCtx, pstCipher, NULL, stMeta.stCipher.szKey, stMeta.stCipher.szIv, 0);
        if (ERR_COMMON_SUCCESS != iErr)
        {
            LOCKET_LOGERROR ("EVP_CipherInit_ex failed. %s", LOCKET_ERR_GetString());
            break;
        }

        /* 分段解密 */
        int i = 0, iReadLen = 0, iOutLen = 0; 
        unsigned long long udlTotalLen = 0;
        long long dlOverSize = 0;
        float fProgress = 0.0;
        for (i = 0; i < iBlocks; i++)
        {
            iReadLen = LOCKET_ReadFile (pucInputBuffer, 1, stMeta.iBlockSize, fp);
            if (iReadLen <= 0)
            {
                /* read failed or read EOF */
                break;
            }
            
            /* 最后几个字节是密文尾部标识符, 不能读进去 */
            udlTotalLen += iReadLen;
            dlOverSize = udlTotalLen - udlCipherDataSize;
            if ((dlOverSize <= ENCRYPT_TAG_LEN) && (dlOverSize >= 0))
            {
                iReadLen -= (int) dlOverSize;
                i = iBlocks - 1;
            }

            iErr = EVP_CipherUpdate(pstCipherCtx, pucOutputBuffer, &iOutLen, pucInputBuffer, iReadLen);
            if (ERR_COMMON_SUCCESS != iErr)
            {
                LOCKET_LOGERROR ("EVP_CipherUpdate failed. %s", LOCKET_ERR_GetString());
                break;
            }

            stParam.pcData    = (char*) pucOutputBuffer;
            stParam.iParamLen  = iOutLen;
            iErr = (*pstFileCrypt->pfCryptCBFunc) (MSG_CRYPT_DATA, (void*) &stParam);
            if (ERR_COMMON_SUCCESS != iErr)
            {
                break;
            }

            if (NULL != pstFileCrypt->pfProgressCBFunc)
            {
                fProgress = GET_PROGRESS (i + 1, iBlocks);
                (*pstFileCrypt->pfProgressCBFunc) (MSG_CRYPT_PROCESS, (void*) &fProgress);
            }
        }

        if (0 == i)
        {
            break;
        }

        int iOutLen2 = 0;
        iErr = EVP_CipherFinal_ex(pstCipherCtx, pucOutputBuffer + iOutLen, &iOutLen2);
        if (ERR_COMMON_SUCCESS != iErr)
        {
            LOCKET_LOGERROR ("EVP_CipherFinal_ex failed. %s", LOCKET_ERR_GetString());
            break;
        }
        stParam.pcData    = (char*) pucOutputBuffer;
        stParam.iParamLen  = iOutLen2;
        (*pstFileCrypt->pfCryptCBFunc) (MSG_CRYPT_DATA, (void*) &stParam);
    } while (0);

    (*pstFileCrypt->pfCryptCBFunc) (MSG_CRYPT_FINISH, (void*) &stParam);

    LOCKET_free ((void**) &pucBuffer);
    EVP_CIPHER_CTX_free (pstCipherCtx);
    fclose (fp);
    
    return iErr;
}

